/*
 * Copyright 2016 SoChishun.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package m.sus;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import util.ICallBack;
import util.URLUtil;
import util.VersionStringComparator;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

/**
 * SUS模型类
 *
 * @author SoChishun
 * @version 1.0 2016-6-9
 */
public class SUSModels {

    final static Logger logger = Logger.getLogger(SUSModels.class.getName());

    final static String DIR_NAME = "./update/";
    /**
     * 文件路径
     */
    final static String FILE_NAME = DIR_NAME + "sus-repository.xml";

    static {
        // 确保目录存在
        File dir = new File(DIR_NAME);
        if (!dir.exists()) {
            dir.mkdir();
            new File(DIR_NAME + "backup/").mkdir();
        }
    }

    /**
     * 把资源文件范例保存到本地目录
     *
     * @since 1.0 2016-6-9 SoChishun Added.
     */
    public static void makeDefault() {
        File file = new File(FILE_NAME);
        if (file.exists()) {
            return; // 已存在就退出
        }
        FileOutputStream outStream = null;
        try {
            int bytesum = 0, byteread = 0;
            // 读取jar包含的菜单资源文件路径
            InputStream inStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("res/sus-repository.xml");
            outStream = new FileOutputStream(file);
            byte[] buffer = new byte[1444];
            while ((byteread = inStream.read(buffer)) != -1) {
                bytesum += byteread; //字节数 文件大小
                outStream.write(buffer, 0, byteread);
            }
            inStream.close();
        } catch (FileNotFoundException ex) {
            logger.log(Level.SEVERE, ex.getMessage());
        } catch (IOException ex) {
            logger.log(Level.SEVERE, ex.getMessage());
        } finally {
            try {
                outStream.close();
            } catch (IOException ex) {
                logger.log(Level.SEVERE, ex.getMessage());
            }
        }
    }

    /**
     * 读取sus-repository.xml
     *
     * @param name
     * @return
     * @since 1.0 2016-6-1 SoChishun Added.
     */
    public static List readLocalXML(String name) {
        if (null == name || name.length() < 1) {
            name = "update-description-files";
        }
        makeDefault(); // 确保文件存在

        SAXReader saxReader = new SAXReader();
        Document document = null;
        try {
            document = saxReader.read(new File(FILE_NAME));
        } catch (DocumentException ex) {
            logger.log(Level.SEVERE, "File sus-repository.xml not found!", ex);
        }
        // 获取根元素
        Element root = document.getRootElement();
        // 获取所有子元素
        List<Element> urlList = root.element(name).elements("url");
        // 迭代输出
        String urlString;
        ArrayList<String> list = new ArrayList<String>();
        for (Element urlNode : urlList) {
            urlString = urlNode.getText();
            if (urlString.length() < 1) {
                continue;
            }
            list.add(urlString);
            System.out.println(urlString);
        }
        return list;
    }
    /**
     * sus-update.xml根节点
     */
    static Element updateXMLRoot;

    /**
     * 判断是否新版本
     *
     * @return
     * @since 1.0 2016-6-9 SoChishun Added.
     */
    public static boolean isNewVersion(String version) {
        String ver_remote = updateXMLRoot.element("update").attributeValue("version");
        return new VersionStringComparator().compare(ver_remote, version) > 0;
    }

    /**
     * 获取版本消息
     *
     * @param locale 语言标签(zh_CN,en_US)
     * @return
     */
    public static String getVersionMessage(String locale) {
        Element element = updateXMLRoot.element("update").element("message").element(locale.toLowerCase());
        return element.getText();
    }

    /**
     * 下载并更新
     *
     * @since 1.0 2016-6-9 SoChishun Added.
     */
    public static void doUpdate() {
        // 异步操作
        new Thread() {
            @Override
            public void run() {
                // 第一步：异步下载文件
                downloadFiles();
                // 第二步：关闭相关进程
                killTheads();
                // 第三步：把旧文件移动到./update/recover/备份
                backupFiles();
                // 第四步：复制文件到目的地
                copyFiles();
                try {
                    // 第五步：启动相关进程
                    execTheads();
                } catch (IOException ex) {
                    logger.log(Level.SEVERE, ex.getMessage());
                }
            }
        }.start();

    }

    /**
     * 启动相关进程
     *
     * @since 1.0 2016-6-14 SoChishun Added.
     */
    static void execTheads() throws IOException {
        Element eleUpdate = updateXMLRoot.element("update");
        List<Element> killElements = eleUpdate.element("startup").elements("file");
        String txtElement;
        File file;
        for (Element itemElement : killElements) {
            txtElement = itemElement.getTextTrim();
            if (txtElement.length() > 0) {
                file = new File(txtElement);
                if (file.exists()) {
                    Runtime.getRuntime().exec("cmd /c " + txtElement);
                }
            }
        }
    }

    /**
     * 从[./]目录备份文件到[./update/backup/]目录
     *
     * @since 1.0 2016-6-14 SoChishun Added.
     */
    static void backupFiles() {
        Element eleUpdate = updateXMLRoot.element("update");
        List<Element> fileElements = eleUpdate.element("files").elements("file");
        String txtPath;
        File file;
        for (Element itemElement : fileElements) {
            txtPath = itemElement.attributeValue("path");
            file = new File(txtPath);
            if (file.exists()) {
                file.renameTo(new File(DIR_NAME + "backup/" + txtPath));
            }
        }
    }

    /**
     * 从[./update/]目录复制文件到[./]目录
     */
    static void copyFiles() {
        Element eleUpdate = updateXMLRoot.element("update");
        List<Element> fileElements = eleUpdate.element("files").elements("file");
        String txtPath;
        File file;
        for (Element itemElement : fileElements) {
            txtPath = itemElement.attributeValue("path");
            file = new File(DIR_NAME + txtPath);
            if (file.exists()) {
                file.renameTo(new File(txtPath));
            }
        }
    }

    /**
     * 关闭相关进程
     *
     * @since 1.0 2016-6-14 SoChishun Added.
     */
    static void killTheads() {
        Element eleUpdate = updateXMLRoot.element("update");
        List<Element> killElements = eleUpdate.element("kill-threads").elements("thread");
        String txtElement;
        for (Element itemElement : killElements) {
            txtElement = itemElement.getTextTrim();
            if (txtElement.length() > 0) {
                killProcessWin(txtElement);
            }
        }
    }

    /**
     * 异步下载文件
     *
     * @return
     * @since 1.0 2016-6-9 SoChishun Added.
     */
    synchronized static boolean downloadFiles() {
        boolean bResult = false;
        List<Element> fileElements = updateXMLRoot.element("update").element("files").elements("file");
        for (Element itemElement : fileElements) {
            URLUtil.saveFile(itemElement.attributeValue("url"), DIR_NAME + itemElement.attributeValue("path"));
        }
        return bResult;
    }

    /**
     * 关闭指定的进程(Windows操作系统)
     *
     * @param name
     * @return
     * @since 1.0 2016-6-9 SoChishun Added.
     * @see http://blog.csdn.net/oscar999/article/details/14291757
     * @see http://my.oschina.net/u/1778309/blog/346958
     */
    static boolean killProcessWin(String name) {
        Process proc = null;
        try {
            proc = Runtime.getRuntime().exec("tasklist");
            BufferedReader br = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            String info = br.readLine();
            while (info != null) {
                if (info.indexOf(name) > -1) {
                    Runtime.getRuntime().exec("taskkill /f /im " + name);
                    return true;
                }
                info = br.readLine();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            proc.destroy();
        }
        return false;
    }

    /**
     * 获取远程sus-update.xml
     *
     * @return
     * @since 1.0 2016-6-9 SoChishun Added.
     */
    public synchronized static boolean getRemoteUpdateXML() {
        Document document = null;
        SAXReader saxReader = new SAXReader();
        List<String> urlList = readLocalXML("update-description-files");
        for (String urlString : urlList) {
            try {
                document = saxReader.read(new URL(urlString));
                if (null != document) {
                    // 获取根元素
                    updateXMLRoot = document.getRootElement();
                    break;
                }
            } catch (DocumentException ex) {
                logger.log(Level.SEVERE, ex.getMessage());
            } catch (MalformedURLException ex) {
                logger.log(Level.SEVERE, ex.getMessage());
            }
        }
        return null != document;
    }

    /**
     * 保存远程sus-repository.xml到本地
     *
     * @return
     * @since 1.0 2016-6-9 SoChishun Added.
     */
    public synchronized static boolean saveRemoteRepositoryXML() {
        boolean bResult = false;
        List<String> urlList = readLocalXML("update-repository-files");
        for (String urlString : urlList) {
            if (URLUtil.saveFile(urlString, FILE_NAME)) {
                bResult = true;
                break;
            }
        }
        return bResult;
    }

    public static boolean DoReadResult;

    /**
     * 异步操作
     *
     * @param callSuccess
     * @param callFail
     * @since 1.0 2016-6-9 SoChishun Added.
     * @see http://www.blogjava.net/tbwshc/archive/2012/07/31/384442.html
     */
    public static void doRead(ICallBack callSuccess, ICallBack callFail) {
        DoReadResult = false;
        new Thread() {
            @Override
            public void run() {
                if (getRemoteUpdateXML()) {
                    callSuccess.execute();
                    return;
                } else if (saveRemoteRepositoryXML()) { // 如果所有sus-update.xml的远程链接都无效,则重新获取远程sus-repository.xml
                    if (getRemoteUpdateXML()) { // 重新获取sus-update.xml
                        callSuccess.execute();
                        return;
                    }
                }
                callFail.execute();
            }
        }.start();
    }
}
